﻿using System;
using System.Linq;

namespace Simp.CodeModel
{
	/// <summary>
	/// Special function refering another function.  It allows to either call the refered function with parameters
	/// or use it as a function (with no parameters).
	/// </summary>
	public class FunctionReference : IFunction, IFunctionSerializer
	{
		private readonly IFunction _innerFunction;

		/// <summary>Construct a function reference.</summary>
		/// <param name="innerFunction"></param>
		public FunctionReference(IFunction innerFunction)
		{
			if (innerFunction == null)
			{
				throw new ArgumentNullException("innerFunction");
			}
			_innerFunction = innerFunction;
		}

		/// <summary>Returns the inner function.</summary>
		public IFunction InnerFunction
		{
			get { return _innerFunction; }
		}

		#region IFunction Members
		string IFunction.FunctionName
		{
			get { return _innerFunction.FunctionName; }
		}

		string[] IFunction.ParameterNames
		{
			get { return _innerFunction.ParameterNames; }
		}

		IFunctionParameterBinding[] IFunction.DefaultParameterValues
		{
			get { return _innerFunction.DefaultParameterValues; }
		}

		IFunction IFunction.Simplify(SimplificationMode simplificationMode, params IFunction[] parameters)
		{
			if (parameters == null)
			{
				throw new ArgumentNullException("parameters");
			}
			if (parameters.Length != 0 && parameters.Length != _innerFunction.ParameterNames.Length)
			{
				throw new ArgumentException("Improper number of parameters", "parameters");
			}
			if (parameters.Length == _innerFunction.ParameterNames.Length)
			{	//	Pass the simplification to the underlying function, since the number of parameters fit
				IFunction boundFunctionCalling = new BoundFunctionCalling(_innerFunction, parameters);

				return FunctionHelper.ApplySimplification(simplificationMode, boundFunctionCalling);
			}
			else
			{	//	Function reference:  no simplification
				return null;
			}
		}

		IFunction IFunction.AcceptVisitor(IFunctionVisitor visitor)
		{
			IFunction visitedInnerFunction = visitor.Visit(_innerFunction);

			if (visitedInnerFunction == null)
			{
				return null;
			}
			else
			{
				return new FunctionReference(visitedInnerFunction);
			}
		}
		#endregion

		#region IFunctionSerializer Members
		void IFunctionSerializer.Serialize(AppendOnlyStringBuilder builder, params IFunction[] parameters)
		{
			CustomFunction customInnerFunction = _innerFunction as CustomFunction;

			if (customInnerFunction == null)
			{
				builder.Append(((IFunction)this).FunctionName);
				builder.Append("(.)");
			}
			else
			{
				IFunctionSerializer innerSerializer = customInnerFunction;

				innerSerializer.Serialize(builder);
			}
		}
		#endregion
	}
}